using System;
using System.IO;
using System.Collections;
using System.Reflection;
using Microsoft.RuleEngine;
using Microsoft.BizTalk.RuleEngineExtensions;

namespace DeployRules
{
	partial class App
	{

		/// <summary>
		/// The ParseCommandLine method parses the arguments passed to the program, and if
		///		/i is specified - sets the importFlag to true
		///		/e is specified - sets the exportFlag to true
		///		/h or /? or no arguments - sets the help flag to true
		///		/l:logfilename - Invokes the ParseCommandLineForLogFileName method to 
		///		extract the name of the log file. 
		/// Raises an ArgumentException if 
		///		- Both the importFlag and exportFlags are set to true and help is not set to true.
		///		- Neither importFlag nor exportFlag are set to true and help is not set to true. 
		/// Finally, if importFlag is true - invokes ParseCommandLineForImport method to parse
		/// import specific options. If exportFlag is set to true - invokes ParseCommandLineForExport
		/// to prase export specific options.  
		/// </summary>
		/// <param name="args">command line arguments passed to the program</param>
		/// <returns></returns>
		private static void ParseCommandLine(string[] args)
		{
			if (args.Length == 0)
			{
				App.help = true;
				return;
			}

			//Recurse through the arguments, and find if we are importing or exporting
			foreach (string arg in args)
			{
				if (arg.Length > 1)
				{
					if (arg[0] == '/' || arg[0] == '-')
					{
						string param = arg.ToLower();
						if (param[1] == 'i')
						{
							// /i - importFlag
							App.importFlag = true;
                            //import policies only
						    App.isPolicyImport = true;
						}
                        else if(param[1] == 'm')
                        {
                            //m  - import vocabularies only
                            App.importFlag = true;
                            App.isVocabularyImport = true;
                        }
						else if (param[1] == 'e')
						{
							// /e - exportFlag
							App.exportFlag = true;
						}
						else if (param[1] == 'u')
						{
							// /u - undeploy policies (1 or more)
							App.undeployPolicies = true;
						}
						else if (param[1] == 'd')
						{
							// /d - deploy policies (1 or more)
							App.deployPolicies = true;
						}
						else if (param[1] == 'r')
						{
							// /d - deploy policies (1 or more)
							App.removeFlag = true;
						}
						else if (param[1] == 'h' || param[1] == '?')
						{
							//if the program is invoked with /h or /?, set the help flag to true
							App.help = true;
							return;
						}
						else if (param[1] == 'l' && param.Length > 2)
						{
							//get the log file name
							ParseCommandLineForLogFileName(param);
						}
					}
				}
			}

			// both import and export flags can not be specified 
			if (App.importFlag && App.exportFlag)
			{
				throw new ArgumentException("Both import and export flags cannot be specified");
			}

			if (!App.importFlag && !App.exportFlag && !App.undeployPolicies && !App.deployPolicies && !App.removeFlag)
			{
				throw new ArgumentException("Specify at least one of the Import, Export, Deploy, Undeploy, and Remove flags");
			}

			if (App.importFlag)
				ParseCommandLineForImport(args);
			if (App.exportFlag)
				ParseCommandLineForExport(args);
			if (App.undeployPolicies)
				ParseCommandLineForUndeploy(args);
			if (App.deployPolicies && !App.importFlag)
				ParseCommandLineForDeploy(args);
			if (App.removeFlag && !App.undeployPolicies)
				ParseCommandLineForRemove(args);
		}

		/// <summary>
		/// The ParseCommandLineForImport deals with the following import flags
		/// Publish - /P
		/// Deploy - /D
		/// Log - /L (/L:LOGFILENAME)
		/// if /p flag is specified, publishPolicy and publishVocabulary flags are
		/// set to true. If /d flag is specified, importDeployPolicy, publishPolicy, and 
		/// publishVocabulary flags to true. 
		/// If an argument is specified with no / prefix, invokes ParseCommandLineForIOFileNames
		/// to extract the names the input BRL XML files. 
		/// </summary>
		/// <param name="args">command line argument array</param>
		/// <returns></returns>
		private static void ParseCommandLineForImport(string[] args)
		{
			foreach (string arg in args)
			{
				if (arg.Length > 1)
				{
					if (arg[0] == '/' || arg[0] == '-')
					{
						//conver the paramater to lowercase string
						string param = arg.ToLower();

						if (param[1] == 'p') // -p is for pubish (import) or policies (export)
						{
							App.publishVocabulary = true;
							App.publishPolicy = true;
						}
						else if (param[1] == 'd')
						{
							App.importDeployPolicy = true;
							App.publishPolicy = true;
							App.publishVocabulary = true;
						}
					}
					else
					{
						ParseCommandLineForIOFileNames(arg);
					}
				}
			}
		}

		/// <summary>
		/// The ParseCommandLineForExport deals with the following export flags
		/// Polices - /P (/P for all policies, /P:POLICY NAME)
		/// Vocabularies - /V (/V for all vocabularies, /V:VOCABULARY NAME)
		/// Log - /L (/L:LOGFILENAME)
		/// if /p flag is specified and policy name is not specified - exportAllPolicies
		/// flag is set to true. If /p flag specified and policy name is specified - name of the policy
		/// is assigned to the policyName field. The same logic applies to major version and minor
		/// version of the policy. 
		/// 
		/// if /v flag is specified and vocabulary name is not specified - exportAllVocabularies
		/// flag is set to true. If /v flag specified and vocabulary name is specified - name of the 
		/// vocabulary is assigned to the vocabularyName field. The same logic applies to major version 
		/// and minor version of the vocabulary. 
		/// 
		/// If an argument is specified with no / prefix, invokes ParseCommandLineForIOFileNames
		/// to extract the names the output BRL XML file. 
		/// </summary>
		/// <param name="args">command line argument array</param>
		/// <returns></returns>
		private static void ParseCommandLineForExport(string[] args)
		{
			foreach (string arg in args)
			{
				if (arg.Length > 1)
				{
					if (arg[0] == '/' || arg[0] == '-')
					{
						//conver the paramater to lowercase string
						string param = arg.ToLower();
						if (param[1] == 'p') // -p is for policies (export)
						{
							//if we are exporting, support /P:<Policy Name>.<Major Version>.<Minor Version>
							if (param.Length > 2 && param[2] == ':')
							{
								//Policy name starts at the 4th position after /P:
								string polNameWithVersion = arg.Substring(3);

								//<Policy Name>[.<Major Version>][.<Minor Version>]

								ExtractPolicyNameMajorMinor(polNameWithVersion);
							}
							else
							{
								//Only /p is specified, no specific policy name
								exportAllPolicies = true;

                                if (args.Length > 2 && Directory.Exists(args[2]))
                                    exportToAPath = true;
							}
						}
						else if (param[1] == 'v')
						{
                            if (param.Length > 2 && param[2] == ':')
                            {
                                string vocNameWithVersion = arg.Substring(3);
                                //<Vocabulary Name>.<Major Version>.<Minor Version>
                                ExtractVocabularyNameMajorMinor(vocNameWithVersion);
                            }
                            else
                            {                                
                                exportAllVocabularies = true;

                                if (args.Length > 2 && Directory.Exists(args[2]))
                                    exportToAPath = true;
                            }
						}
					}
					else
					{
						ParseCommandLineForIOFileNames(arg);
					}
				}
			}
		}

		/// <summary>
		/// The ParseCommandLineForIOFileNames builds App.files array with the
		/// strings containing names of the files that were passed as command-line
		/// arguments. 
		/// Syntax suppoted: 
		///		FILE NAME
		///		FILE NAME1, FILE NAME2, FILE NAME3 ETC...
		///		PARTIAL FILE NAME*
		///		PARTIAL FILE NAME WITH ONE LETTER MISSING?
		/// </summary>
		/// <param name="args">command line argument passed without any / prefix</param>
		/// <returns></returns>
		private static void ParseCommandLineForIOFileNames(string arg)
		{

			if (arg.IndexOf('*') >= 0 || arg.IndexOf('?') >= 0)
			{
				string dir = Path.GetDirectoryName(arg);
				string filename = null;

				if (dir == null || dir.Length == 0)
				{
					dir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
					filename = arg;
				}
				else
					filename = Path.GetFileName(arg);

				string[] wildFiles = Directory.GetFiles(dir, filename);

				if (wildFiles.Length == 0)
					throw new FileNotFoundException(string.Format("The {0} BRL files do not exist.", filename));

				App.files.AddRange(wildFiles);
			}
			else
			{
				if (App.importFlag && !File.Exists(arg))
					throw new FileNotFoundException(string.Format("The {0} BRL file does not exist.", arg));

				App.files.Add(arg);
			}
		}

		/// <summary>
		/// The ParseCommandLineForLogFileNames gets the name of the log file
		/// from the command line argment that has the sytax as shown in the following line:
		///		/L:LOGFILENAME
		/// </summary>
		/// <param name="args">command line argument passed with /L: prefix</param>
		/// <returns></returns>
		private static void ParseCommandLineForLogFileName(string param)
		{
			if (App.logWriter == null)
			{
				// /l:<FileName>
				string logFilename = param.Substring(3);
				if (logFilename != null && logFilename.Length > 0)
				{
					if (File.Exists(logFilename))
					{
						Console.WriteLine(logFilename + " already exists. Appending to the log file.");
					}
					App.logWriter = new StreamWriter(logFilename, true);
					App.logWriter.WriteLine("======================================");
					App.logWriter.WriteLine("{0}", GetTitle());
					App.logWriter.WriteLine("Date time stamp: " + DateTime.Now);
				}
			}
			else
				throw new ApplicationException("Log file already specified.");
		}

		/// <summary>
		/// The ParseCommandLineForUndeploy deals with the following undeploy flags
		/// /R - Remove after undeploy
		/// /P - Policy name
		/// Sample syntaxes: 
		///		DeployRules /u /P:MyPolicy
		///		DeployRules /u /P:MyPolicy /l:MyLogFile.txt
		///		DeployRules /u /P:MyPolicy /P:MyOtherPolicy /l:MyLogFile.txt
		///		DeployRules /u /P:MyPolicy /r
		/// </summary>
		/// <param name="args">command line argument array</param>
		/// <returns></returns>
		private static void ParseCommandLineForUndeploy(string[] args)
		{
			foreach (string arg in args)
			{
				if (arg.Length > 1)
				{
					if (arg[0] == '/' || arg[0] == '-')
					{
						//conver the paramater to lowercase string
						string param = arg.ToLower();

						if (param[1] == 'r') // -p is for pubish (import) or policies (export)
						{
							App.removeFlag = true;
						}
						else if (param[1] == 'p') // -p is for policies 
						{
							//if we are exporting, support /P:<Policy Name>.<Major Version>.<Minor Version>
							if (param.Length > 2 && param[2] == ':')
							{
								//Policy name starts at the 4th position after /P:
								string polNameWithVersion = arg.Substring(3);
								
                                //Add the name of the policy to the list of policies
								//policies.Add(polNameWithVersion);
                                AddPolicy(polNameWithVersion);
							}
						}

					}
				}
			}
		}

		/// <summary>
		/// The ParseCommandLineForDeploy deals with the following deploy flags
		/// /P - Policy name
		/// Sample syntaxes: 
		///		DeployRules /d /P:MyPolicy
		///		DeployRules /d /P:MyPolicy /l:MyLogFile.txt
		///		DeployRules /d /P:MyPolicy /P:MyOtherPolicy /l:MyLogFile.txt
		/// </summary>
		/// <param name="args">command line argument array</param>
		/// <returns></returns>
		private static void ParseCommandLineForDeploy(string[] args)
		{
			foreach (string arg in args)
			{
				if (arg.Length > 1)
				{
					if (arg[0] == '/' || arg[0] == '-')
					{
						//conver the paramater to lowercase string
						string param = arg.ToLower();
						if (param[1] == 'p') // -p is for policies 
						{
							//if we are exporting, support /P:<Policy Name>.<Major Version>.<Minor Version>
							if (param.Length > 2 && param[2] == ':')
							{
								//Policy name starts at the 4th position after /P:
								string polNameWithVersion = arg.Substring(3);

                                //Add the name of the policy to the list of policies
                                //policies.Add(polNameWithVersion);
                                AddPolicy(polNameWithVersion);
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// The ParseCommandLineForRemove deals with the following remove flags
		/// /P - Policy name
		/// /V - Vocabulary name
		/// Sample syntaxes: 
		///		DeployRules /r /P:MyPolicy
		///		DeployRules /r /P:MyPolicy /l:MyLogFile.txt
		///		DeployRules /r /P:MyPolicy /P:MyOtherPolicy /l:MyLogFile.txt
		///		DeployRules /r /P:MyPolicy /v:MyVocabulary
		///		DeployRules /r /P:MyVocabulary
		/// </summary>
		/// <param name="args">command line argument array</param>
		/// <returns></returns>
		private static void ParseCommandLineForRemove(string[] args)
		{
			foreach (string arg in args)
			{
				if (arg.Length > 1)
				{
					if (arg[0] == '/' || arg[0] == '-')
					{
						//conver the paramater to lowercase string
						string param = arg.ToLower();
						if (param[1] == 'p') // -p is for policies (export)
						{
							//if we are exporting, support /P:<Policy Name>.<Major Version>.<Minor Version>
							if (param.Length > 2 && param[2] == ':')
							{
								//Policy name starts at the 4th position after /P:
								string polNameWithVersion = arg.Substring(3);
								
                                //policies.Add(polNameWithVersion);
                                AddPolicy(polNameWithVersion);
							}
						}
						else if (param[1] == 'v')
						{
							if (param.Length > 2 && param[2] == ':')
							{
								string vocNameWithVersion = arg.Substring(3);
								
                                //vocabularies.Add(vocNameWithVersion);
                                AddVocabulary(vocNameWithVersion);
							}
						}
					}
				}
			}
			if ((vocabularies.Count == 0) && (policies.Count == 0))
				LogMessage("No vocabularies or policies specified. Make sure you use /P: or /V: prefix");
		}

        private static void AddPolicy(string argPolicyName)
        {
            //If * then get all policies in BRE
            if (argPolicyName == "*")
            {
                RuleStore breStore;

                Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver breDriver = new Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver();
                
                breStore = breDriver.GetRuleStore();

                RuleSetInfoCollection ruleSetColl =  breStore.GetRuleSets(RuleStore.Filter.All);

                foreach (RuleSetInfo ruleSetInfo in ruleSetColl)
                {
                    Console.WriteLine("{0}.{1}.{2}",ruleSetInfo.Name ,ruleSetInfo.MajorRevision ,ruleSetInfo.MinorRevision);
                    
                    policies.Add(ruleSetInfo.Name);
                }
                Console.Out.WriteLine("\n");
            }
            else //otherwise add the policy with the name provided
            {
                App.isSingleDeployment = true;
                policies.Add(argPolicyName);
            }
        }

        private static void AddVocabulary(string argVocabularyName)
        {
            //If * then get all policies in BRE
            if (argVocabularyName == "*")
            {
                RuleStore breStore;

                Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver breDriver = new Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver();

                breStore = breDriver.GetRuleStore();

                VocabularyInfoCollection vocabSetColl = breStore.GetVocabularies(RuleStore.Filter.All);

                foreach (VocabularyInfo vocabInfo in vocabSetColl)
                {
                    //We don't want to remove Microsoft's provided Vocabularies
                    //In case you are also running the ESB Guidance, we do not want to remove
                    //those vocabularies
                    if (vocabInfo.ModifiedBy != "Microsoft" && (!vocabInfo.Name.StartsWith("ESB.")))
                    {
                        vocabularies.Add(vocabInfo.Name);
                    }
                }
            }
            else //otherwise add the policy with the name provided
                vocabularies.Add(argVocabularyName);
        }








        




	}
}
